<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { NButton, NInput, NPopover, NTag, useMessage } from 'naive-ui'
import AiMsg from './aiMsg.vue'
import { SvgIcon } from '@/components/common'
import { useBasicLayout } from '@/hooks/useBasicLayout'
import { mlog, upImg } from '@/api'
import { homeStore } from '@/store'
import { t } from '@/locales'
import axios from '@/utils/request/axios'
import { getToken } from '@/store/modules/auth/helper'
import { submitComfyUI, getComfyWs, getComfyServer } from '@/api/seed'
const $emit = defineEmits(['drawSent', 'close', 'furnitureSelected', 'requestCanvasImage'])
const { isMobile } = useBasicLayout()

const showMoreParams = ref(false)

const f = ref({ bili: -1, quality: '', view: '', light: '', shot: '', style: '', styles: '', version: '--v 6.0', sref: '', cref: '', cw: '' })

const st = ref({
  text: 'Remove all movable furniture from the room, retaining only the wall material, windows, and floor material, turning it into an empty room. Remove the trapezoidal object from the floor.',
  mergeText: 'Maintain the scene unchanged, keep the furniture style, material, and pattern unchanged, and make the image as realistic as possible. Convert this image into a real-life scene with a photo effect. Repair the white edges of the furniture to blend into the scene, turn on the floor lamp, table lamp, and chandelier.',
  denoise: 0.3,
  pixelSize: 6,
  isDisabled: false,
  isLoad: false,
  fileBase64: [] as any[],
  furnitureFileBase64: [] as any[], // 软装设计场景专用
  bot: '',
  showFace: false,
  upType: '',
})

const msgRef = ref()
const fsBaseRef = ref()
const fsFurnitureRef = ref() // 软装设计场景专用
const fsRef = ref()
const fsRef2 = ref()
const fsRef3 = ref()
const isDisabled = computed(() => {
  return st.value.fileBase64.length <= 0 || st.value.isLoad
})
const ms = useMessage()

const taskIdText = ref('')

function deleteFurnitureFile() {
  st.value.furnitureFileBase64 = []
  $emit('drawSent', { action: 'clearFurniture' })
}

function deleteFileBase() {
  st.value.fileBase64 = []
  $emit('drawSent', { action: 'clear' })
}

async function create() {
  if (st.value.fileBase64.length <= 0) {
    ms.error('请先上传客户家装场景')
    return
  }
  if (st.value.furnitureFileBase64.length <= 0) {
    ms.error('请先上传软装设计场景')
    return
  }

  const response = await fetch('/workflow/1.json')
  if (!response.ok) {
    ms.error('获取工作流失败！')
    return
  }
  const workflowData = await response.json()
  if (!workflowData) {
    ms.error('获取工作流失败！')
    return
  }

  const submitResponse = await submitComfyUI({
    baseImage: st.value.fileBase64[0].url,
    furnitureImage: st.value.furnitureFileBase64[0].url,
    prompt: workflowData,
  })

  if (!submitResponse) {
    ms.error('提交任务失败！')
    return
  }

  mlog('submitResponse: %s', JSON.stringify(submitResponse))

  const promptId = submitResponse.prompt_id
  if (!promptId) {
    ms.error('提交任务失败！')
    return
  }

  taskIdText.value = promptId

  const wsURI = (await getComfyWs()).data
  const ws = new WebSocket(wsURI)
  ws.onopen = () => {
    mlog('ComfyUI WebSocket 连接已打开')
  }

  ws.onmessage = (event) => {
    mlog('收到ComfyUI服务器消息:', event.data)
    const message = JSON.parse(event.data)
    if (message.type === 'executing' && message.data.node === null && message.data.prompt_id === promptId) {
      mlog('任务执行完成！')
      ws.close()
      fetchOutputImages(promptId)
    }
  }

  ws.onerror = (error) => {
    mlog('ComfyUI WebSocket 错误:', error)
  }

  ws.onclose = () => {
    mlog('ComfyUI WebSocket 连接已关闭')
  }
}

async function fetchOutputImages(promptId: string) {
  const comyfyui = (await getComfyServer('/view')).data
  const server = (await getComfyServer(`/history/${promptId}`)).data
  const response = await axios.get(server)
  const outputs = response.data[promptId].outputs

  const images = []
  for (const nodeId in outputs) {
    if (outputs[nodeId].images) {
      for (const imageInfo of outputs[nodeId].images) {
        const imageUrl = `${comyfyui}?filename=${imageInfo.filename}&subfolder=${imageInfo.subfolder}&type=${imageInfo.type}`
        images.push(imageUrl)
      }
    }
  }

  $emit('drawSent', { action: 'result', images })
}

function selectFileBase(input: any) {
  if (st.value.fileBase64.length >= 5) {
    ms.error(t('mjchat.more5sb'))
    return
  }
  upImg(input.target.files[0]).then((d: any) => {
    const index = st.value.fileBase64.findIndex(v => v == d)
    if (index > -1) {
      ms.error(t('mjchat.no2add'))
      return
    }
    st.value.fileBase64.push(d)
    fsBaseRef.value.value = ''
    st.value.upType = 'base'
    $emit('drawSent', st.value)
  }).catch(e => msgRef.value.showError(e))
}

function selectFurnitureFile(input: any) {
  if (st.value.furnitureFileBase64.length >= 5) {
    ms.error(t('mjchat.more5sb'))
    return
  }
  upImg(input.target.files[0]).then((d: any) => {
    const index = st.value.furnitureFileBase64.findIndex(v => v == d)
    if (index > -1) {
      ms.error(t('mjchat.no2add'))
      return
    }
    st.value.furnitureFileBase64.push(d)
    fsFurnitureRef.value.value = ''
    st.value.upType = 'furniture'
    $emit('drawSent', st.value)
  }).catch(e => msgRef.value.showError(e))
}

function selectFile(input: any) {
  if (st.value.fileBase64.length >= 5) {
    ms.error(t('mjchat.more5sb'))
    return
  }
  upImg(input.target.files[0]).then((d: any) => {
    const index = st.value.fileBase64.findIndex(v => v == d)
    if (index > -1) {
      ms.error(t('mjchat.no2add'))
      return
    }
    st.value.fileBase64.push(d)
    fsRef.value.value = ''
  }).catch(e => msgRef.value.showError(e))
}

// 图生文
function selectFile2(input: any) {
  upImg(input.target.files[0]).then((d) => {
    mlog('f2base64>> ', d)
    const obj = {
      action: 'img2txt',
      data: {
        base64: d,
        botType: 'MID_JOURNEY',
      },
    }
    homeStore.setMyData({ act: 'draw', actData: obj })
    // input.value.value='';
    fsRef2.value.value = ''
  })
    .catch(e => msgRef.value.showError(e))
}

onMounted(() => {
  // queryHC28List().then((d) => {
  //   mlog('queryHC28List>> ', d)
  //   console.log('queryHC28List>> ', d)
  //   furnitureList.value = d
  //   console.log('queryHC28List>> ', furnitureList.value)
  // }).catch(e => msgRef.value.showError(e))
})

const showTaskById = (taskId: string) => {
  mlog('showTaskById>> ', taskId)
  // window.open(`https://www.midjourney.com/app/result/${taskId}`);
}

const clearAll = () => {
  st.value.fileBase64 = []
  st.value.text = ''
  f.value.bili = -1
  f.value.version = ''
  f.value.quality = ''
  f.value.shot = ''
  f.value.light = ''
  f.value.style = ''
  f.value.styles = ''
  f.value.view = ''
  f.value.cref = ''
  f.value.cw = ''
  f.value.sref = ''

  taskIdText.value = ''
  $emit('drawSent', { action: 'clear' })
}

const selectFile3 = (input: any) => {
  // ms.loading('上传中...');
  upImg(input.target.files[0]).then(async (d) => {
    mlog('selectFile3>> ', d)
    const data = {
      action: 'img2txt',
      data: {
        base64Array: d,
      },
    }
    const blob = base64ToBlob(data.data.base64Array)
    const file = blobToFile(blob, 'image.png') // 指定文件名和扩展名
    // 创建FormData
    const formData = new FormData()
    formData.append('file', file)
    mlog('formData========', formData)
    try {
      const result = await axios.post('/resource/oss/upload', formData, {
        headers: {
          'Content-Type': 'multipart/form-data',
          'Authorization': `Bearer ${getToken()}`,
        },
      })
      fsRef3.value.value = ''
      if (result.data.code == 200) {
        if (st.value.upType == 'cref')
          f.value.cref = result.data.data.url
        else
          f.value.sref = result.data.data.url

        ms.success(t('mj.uploadSuccess'))
      }
      else {
        ms.error(t(result.data.msg))
      }
      mlog('selectFile3>> ', d)
    }
    catch (e) {
      msgRef.value.showError(e)
    }
  })
    .catch(e => msgRef.value.showError(e))
}

// 将base64字符串转换为Blob对象
function base64ToBlob(base64: string): Blob {
  const byteString = window.atob(base64.split(',')[1])
  const mimeString = base64.split(',')[0].split(':')[1].split(';')[0]
  const ab = new ArrayBuffer(byteString.length)
  const ia = new Uint8Array(ab)
  for (let i = 0; i < byteString.length; i++)
    ia[i] = byteString.charCodeAt(i)

  return new Blob([ab], { type: mimeString })
}

// 将Blob对象转换为File对象
function blobToFile(blob: Blob, fileName: string): File {
  const file = new File([blob], fileName, { type: blob.type })
  return file
}
</script>

<template>
  <AiMsg ref="msgRef" />
  <input ref="fsBaseRef" type="file" style="display: none" accept="image/jpeg, image/jpg, image/png, image/gif"
    @change="selectFileBase">
  <input ref="fsFurnitureRef" type="file" style="display: none" accept="image/jpeg, image/jpg, image/png, image/gif"
    @change="selectFurnitureFile">
  <input ref="fsRef" type="file" style="display: none" accept="image/jpeg, image/jpg, image/png, image/gif"
    @change="selectFile">
  <input ref="fsRef2" type="file" style="display: none" accept="image/jpeg, image/jpg, image/png, image/gif"
    @change="selectFile2">
  <input ref="fsRef3" type="file" style="display: none" accept="image/jpeg, image/jpg, image/png, image/gif"
    @change="selectFile3">

  <div class="overflow-y-auto bg-[#fafbfc] px-4 dark:bg-[#18181c] h-full draw-form">
    <section class="mb-4">
      <div class="mr-1  mb-2 flex justify-between items-center">
        <div class="text-sm">
          家装场景
        </div>
        <div>
          <SvgIcon icon="mdi:delete-outline" class="text-lg cursor-pointer" @click="deleteFileBase" />
        </div>
      </div>
      <div class=" flex items-center justify-between space-x-1">
        <div
          class="h-[150px] w-[100%] rounded-sm border border-gray-400/20 flex justify-center items-center cursor-pointer"
          @click="fsBaseRef.click()">
          <img v-if="st.fileBase64.length > 0" :src="st.fileBase64[0].url"
            style="max-width: 100%; max-height: 100%; object-fit: contain;">
          <div v-else class="text-center">
            上传客户家装场景
          </div>
        </div>
      </div>
    </section>

    <section class="mb-4">
      <div class="mr-1  mb-2 flex justify-between items-center">
        <div class="text-sm">
          软装设计场景
        </div>
        <div>
          <SvgIcon icon="mdi:delete-outline" class="text-lg cursor-pointer" @click="deleteFurnitureFile" />
        </div>
      </div>
      <div class=" flex items-center justify-between space-x-1">
        <div
          class="h-[150px] w-[100%] rounded-sm border border-gray-400/20 flex justify-center items-center cursor-pointer"
          @click="fsFurnitureRef.click()">
          <img v-if="st.furnitureFileBase64.length > 0" :src="st.furnitureFileBase64[0].url"
            style="max-width: 100%; max-height: 100%; object-fit: contain;">
          <div v-else class="text-center">
            上传家具拼图
          </div>
        </div>
      </div>
    </section>
    <!-- <section class="mb-4">
      <div class="text-sm">
        放置家具
      </div>
      <div style="margin-top: 10px;">
        <div class="h-[400px] w-[100%] overflow-y-auto p-2 border border-gray-400/20 rounded-sm">
          <el-collapse expand-icon-position="left" accordion>
            <el-collapse-item
              v-for="furnitureCategories in furnitureList" :title="furnitureCategories.name"
              :name="furnitureCategories.name"
            >
              <div style="display: flex; flex-wrap: wrap; gap: 10px;">
                <el-card
                  v-for="item in furnitureCategories.list" style="height: 90px; width: 80px;"
                  @click="selectFurniture(item)"
                >
                  <img :src="item.url" style="width: 100%">
                  <template #footer>
                    {{ item.category }}
                  </template>
</el-card>
</div>
</el-collapse-item>
</el-collapse>
</div>
</div>
</section> -->
    <section class="mb-4">
      <div class="mr-1  mb-2 flex justify-between items-center">
        <div class="text-sm">
          更多参数
        </div>
        <div>
          <SvgIcon icon="mdi:chevron-down" class="text-lg cursor-pointer" @click="showMoreParams = !showMoreParams" />
        </div>
      </div>
      <div class="flex flex-col space-y-3" v-if="showMoreParams">
        <div class="w-[100%]">
          <div class="flex items-center mb-1">
            <span class="text-sm font-medium">空房间生成</span>
            <NPopover trigger="hover">
              <template #trigger>
                <SvgIcon icon="mdi:help-circle-outline" class="ml-1 text-gray-400 cursor-help" />
              </template>
              <span>描述空房间的生成参数，例如光照、风格等</span>
            </NPopover>
          </div>
          <NInput v-model:value="st.text" type="textarea" placeholder="空房间生成" round clearable maxlength="2000"
            show-count :autosize="{ minRows: 2, maxRows: 5 }" />
        </div>
        <div class="w-[100%]">
          <div class="flex items-center mb-1">
            <span class="text-sm font-medium">空房间+软装合并</span>
            <NPopover trigger="hover">
              <template #trigger>
                <SvgIcon icon="mdi:help-circle-outline" class="ml-1 text-gray-400 cursor-help" />
              </template>
              <span>当家具的角度跟原场景的透视图一致，使用下面这段英文描述词：<br>
                Maintain the scene unchanged, keep the furniture style, material, and pattern unchanged, and make the
                image
                as realistic as possible. <br>Convert this image into a real-life scene with a photo effect. Repair the
                white edges of the furniture to blend into the scene, turn on the floor lamp, table lamp, and
                chandelier.
                <br>

                当家具的角度跟原场景的透视图不一致，使用下面这段英文描述词：<br>
                Change the scene, change the furniture, make the perspective of the furniture harmonious with the scene,
                and
                make the image as realistic as possible. <br>Convert it into a real-life scene, a single photo. Repair
                the
                white edges of the furniture to make it blend into the scene, turn on the floor lamp, table lamp, and
                chandelier.</span>
            </NPopover>
          </div>
          <NInput v-model:value="st.mergeText" type="textarea" placeholder="空房间+软装合并" round clearable maxlength="2000"
            show-count :autosize="{ minRows: 2, maxRows: 5 }" />
        </div>
        <div class="w-[100%]">
          <div class="flex items-center mb-1">
            <span class="text-sm font-medium">重绘幅度</span>
            <NPopover trigger="hover">
              <template #trigger>
                <SvgIcon icon="mdi:help-circle-outline" class="ml-1 text-gray-400 cursor-help" />
              </template>
              <span>常用范围0.2-0.5<br>
                重绘幅度数值越小和原图越接近，越大偏差越大。<br>
                1.如果原图分辨率较低，细节（如logo）较糊，重绘幅度调得很小也不能确保100%还原，ai会重绘成接近的元素<br>
                2.可尝试调大重绘幅度来交给ai修正一些画面不协调的元素</span>
            </NPopover>
          </div>
          <NInput v-model:value="st.denoise" type="number" placeholder="重绘幅度" round clearable :min="0" :max="1"
            :step="0.01" />
        </div>
        <div class="w-[100%]">
          <div class="flex items-center mb-1">
            <span class="text-sm font-medium">最终效果图像素大小</span>
            <NPopover trigger="hover">
              <template #trigger>
                <SvgIcon icon="mdi:help-circle-outline" class="ml-1 text-gray-400 cursor-help" />
              </template>
              <span>数值为生成后总像素，比例会按照原图比例保持不变(不知道怎么改就保持默认)<br>

                参考值：<br>
                1024x1024=1 百万 (移动端)<br>
                2048x2048=4 百万 (头图，LED屏)<br>
                3072x3072=8 百万 (线下物料)<br>
                4096x4096=16 百万<br>
                5120x5120=25 百万<br>

                长宽值只做大概参考，不是都变成方形</span>
            </NPopover>
          </div>
          <NInput v-model:value="st.pixelSize" type="number" placeholder="最终效果图像素大小" round clearable :min="1" :max="100"
            :step="1" />
        </div>
      </div>
    </section>

    <div class="flex">
      <NButton type="success" style="border-radius: 12px; height: 48px; line-height: 48px; font-size: 17px;"
        :bordered="false" class="genner-button" :disabled="isDisabled" :block="true" @click="create()">
        <SvgIcon icon="mingcute:send-plane-fill" />
        <template v-if="st.isLoad">
          生成中，请稍后
        </template>
        <template v-else>
          软装一键入户
        </template>
      </NButton>
    </div>
    <div class="flex justify-start items-center py-1">
      <div>
        <NTag type="info" round size="small" style="cursor: pointer; " :bordered="false" @click="clearAll()">
          <div style="display: flex;">
            <SvgIcon icon="ant-design:clear-outlined" />{{ $t('mj.clearAll') }}
          </div>
        </NTag>
        <NTag type="info" round size="small" style="cursor: pointer; " :bordered="false"
          @click="showTaskById(taskIdText)">
          <div style="display: flex;">
            任务编号：{{ taskIdText }}
          </div>
        </NTag>
      </div>
    </div>

    <ul v-if="!isMobile" class="pt-4" style="font-size: 12px;" v-html="$t('mjchat.imginfo')" />
  </div>
</template>

<style>
.aspect-item.active,
.aspect-item.active .aspect-box {
  border-color: #86dfba;

}

.badge-item {
  margin-top: 10px;
  margin-right: 40px;
}

.el-card__footer {
  padding: 5px 0 0 0;
  text-align: center;
}

.el-card__body {
  padding: 0;
}
</style>
